// $Id: array_impl.hpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_DETAIL_ARRAY_IMPL_HPP
#define XPCOM_DETAIL_ARRAY_IMPL_HPP

#ifdef XPCOM_USE_TEMPLATE_ARRAY_IMPL
#include <xpcom/detail/array_impl_templ.hpp>

#else // XPCOM_USE_TEMPLATE_ARRAY_IMPL
#include <xpcom/config.hpp>
#include <cstring> // for std::memset
#include <xpcom/types.hpp>
#include <xpcom/array_traits.hpp>
#include <xpcom/assert.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

namespace xpcom {
namespace detail {

// Element construct/destruct
template <typename Traits>
inline void element_construct_aux(
        typename Traits::element_type* /*first*/,
        typename Traits::element_type* /*last*/,
        construct_policy::void_construct_tag)
{
    // noop
}

template <typename Traits>
inline void element_construct_aux(
        typename Traits::element_type* first,
        typename Traits::element_type* last,
        construct_policy::zero_construct_tag)
{
    XPCOM_ASSERT(first == last || (first && first <= last));
    std::memset(first, 0,
            sizeof(typename Traits::element_type) * (last - first));
}

template <typename Traits>
void element_construct_aux(
        typename Traits::element_type* first,
        typename Traits::element_type* last,
        construct_policy::ctor_construct_tag)
{
    XPCOM_ASSERT(first == last || (first && first <= last));
    while (first != last) {
        Traits::construct(*first);
        ++first;
    }
}

template <typename Traits>
inline void element_construct(
        typename Traits::element_type* first,
        typename Traits::element_type* last)
{
    typedef typename Traits::construct_policy_tag tag;
    element_construct_aux<Traits>(first, last, tag());
}

template <typename Traits>
inline void element_destruct_aux(
        typename Traits::element_type* /*first*/,
        typename Traits::element_type* /*last*/,
        destruct_policy::void_destruct_tag)
{
    // noop
}

template <typename Traits>
void element_destruct_aux(
        typename Traits::element_type* first,
        typename Traits::element_type* last,
        destruct_policy::dtor_destruct_tag)
{
    XPCOM_ASSERT(first == last || (first && first <= last));
    while (first != last) {
        Traits::destruct(*first);
        ++first;
    }
}

template <typename Traits>
inline void element_destruct(
        typename Traits::element_type* first,
        typename Traits::element_type* last)
{
    typedef typename Traits::destruct_policy_tag tag;
    element_destruct_aux<Traits>(first, last, tag());
}

// Unmanaged array data which honors [length_is].
struct XPCOM_DECL array_data
{
    typedef uint32_t size_type;

    void* data_;
    size_type size_;
    size_type length_;

    array_data() throw() :
        data_(0),
        size_(0),
        length_(0)
    {
    }

    array_data(void* data, size_type size, size_type length) throw() :
        data_(data),
        size_(size),
        length_(length)
    {
    }

    array_data(void* data, size_type size) throw() :
        data_(data),
        size_(size),
        length_(size)
    {
    }

    size_type size() const throw()
    {
        return size_;
    }

    size_type length() const throw()
    {
        return length_;
    }
};

// Unmanaged array data which ignores [length_is].
struct XPCOM_DECL array_data2
{
    typedef uint32_t size_type;

    void* data_;
    size_type size_;

    array_data2() throw() :
        data_(0),
        size_(0)
    {
    }

    array_data2(void* data, size_type /*size*/, size_type length) throw() :
        data_(data),
        size_(length)
    {
    }

    array_data2(void* data, size_type size) throw() :
        data_(data),
        size_(size)
    {
    }

    size_type size() const throw()
    {
        return size_;
    }

    size_type length() const throw()
    {
        return size_;
    }
};

// Array base implementation which honors [length_is].
class XPCOM_DECL array_impl_base : public array_data
{
    typedef array_data base_type;
public:
    typedef base_type data_type;
    typedef base_type::size_type size_type;

    array_impl_base() throw() :
        base_type()
    {
    }

    array_impl_base(void* data, size_type size, size_type length) throw() : // takes ownership
        base_type(data, size, length)
    {
    }

    array_impl_base(void* data, size_type size) throw() : // takes ownership
        base_type(data, size)
    {
    }

    array_impl_base(array_impl_base& rhs) throw(); // takes ownership
    array_impl_base(size_type sn, size_type n, bool init = false);

    ~array_impl_base() throw();

    void swap(array_impl_base& rhs) throw();

    void reset() throw();
    void release(void** data, size_type* size, size_type* length) throw();
    void release(void** data, size_type* size) throw();

    void reserve(size_type sn, size_type n);
    void resize(size_type sn, size_type n, bool init = false);

private:
    const array_impl_base& operator=(const array_impl_base&);
};

// Array base implementation which ignores [length_is].
class XPCOM_DECL array_impl_base2 : public array_data2
{
    typedef array_data2 base_type;
public:
    typedef base_type data_type;
    typedef base_type::size_type size_type;

    array_impl_base2() throw() :
        base_type()
    {
    }

    array_impl_base2(void* data, size_type size, size_type length) throw() : // takes ownership
        base_type(data, size, length)
    {
    }

    array_impl_base2(void* data, size_type size) throw() : // takes ownership
        base_type(data, size)
    {
    }

    array_impl_base2(array_impl_base2& rhs) throw(); // takes ownership
    array_impl_base2(size_type sn, size_type n, bool init = false);

    ~array_impl_base2() throw();

    void swap(array_impl_base2& rhs) throw();

    void reset() throw();
    void release(void** data, size_type* size, size_type* length) throw();
    void release(void** data, size_type* size) throw();

    void reserve(size_type sn, size_type n);
    void resize(size_type sn, size_type n, bool init = false);

private:
    const array_impl_base2& operator=(const array_impl_base2&);
};

// Unified types
#ifdef XPCOM_ENABLE_LENGTH_IS
typedef array_impl_base array_impl_base_type;
#else
typedef array_impl_base2 array_impl_base_type;
#endif

// Array implementation.
// TODO Exception safety needs to be checked
template <typename Traits>
class array_impl : public array_impl_base_type
{
    typedef array_impl_base_type base_type;
public:
    typedef base_type::data_type data_type;
    typedef Traits traits_type;
    typedef typename traits_type::element_type element_type;
    typedef base_type::size_type size_type;

    array_impl() :
        base_type()
    {
    }

    array_impl(element_type* data, size_type size, size_type length) :
        base_type(static_cast<void*>(data), size, length) // takes ownership
    {
    }

    array_impl(element_type* data, size_type size) :
        base_type(static_cast<void*>(data), size) // takes ownership
    {
    }

    array_impl(array_impl& rhs) :
        base_type(rhs) // takes ownership
    {
    }

    explicit array_impl(size_type n) :
        base_type(sizeof(element_type), n)
    {
        element_construct<traits_type>(data(), data() + length());
    }

    ~array_impl()
    {
        element_destruct<traits_type>(data(), data() + length());
    }

    element_type* data() const
    {
        return static_cast<element_type*>(data_);
    }

    element_type** data_addr()
    {
        return reinterpret_cast<element_type**>(&data_);
    }

    void swap(array_impl& rhs)
    {
        base_type::swap(rhs);
    }

    void reset()
    {
        element_destruct<traits_type>(data(), data() + length());
        base_type::reset();
    }

    void release(element_type** data, size_type* size, size_type* length)
    {
        base_type::release(reinterpret_cast<void**>(data), size, length);
    }

    void release(element_type** data, size_type* size)
    {
        base_type::release(reinterpret_cast<void**>(data), size);
    }

    void reserve(size_type n)
    {
        base_type::reserve(sizeof(element_type), n);
    }

    void resize(size_type n)
    {
        typedef typename Traits::construct_policy_tag construct_tag;
        typedef typename Traits::destruct_policy_tag destruct_tag;
        resize_aux(n, construct_tag(), destruct_tag());
    }

private:
    void resize_aux(
            size_type n,
            construct_policy::void_construct_tag,
            destruct_policy::void_destruct_tag)
    {
        base_type::resize(sizeof(element_type), n, false);
    }

    void resize_aux(
            size_type n,
            construct_policy::zero_construct_tag,
            destruct_policy::void_destruct_tag)
    {
        base_type::resize(sizeof(element_type), n, true);
    }

    void resize_aux(
            size_type n,
            construct_policy::ctor_construct_tag,
            destruct_policy::dtor_destruct_tag)
    {
        size_type len = length();
        if (n < len) {
            XPCOM_ASSERT(data());
            element_destruct<traits_type>(data() + n, data() + len);
        }

        base_type::resize(sizeof(element_type), n, false);
        XPCOM_ASSERT(length() == n);

        if (n > len) {
            XPCOM_ASSERT(data());
            element_construct<traits_type>(data() + len, data() + n);
        }
    }
};

} // namespace xpcom::detail
} // namespace xpcom

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_USE_TEMPLATE_ARRAY_IMPL

#endif // XPCOM_DETAIL_ARRAY_IMPL_HPP
